package multithread.framework.threadpool;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import multithread.framework.annotation.ThreadAfter;
import multithread.framework.annotation.ThreadAllAfter;
import multithread.framework.annotation.ThreadAllBefore;
import multithread.framework.annotation.ThreadBefore;
import multithread.framework.annotation.ThreadGroupAfter;
import multithread.framework.annotation.ThreadGroupBefore;
import multithread.framework.threadmethod.IThreadMethod;
import multithread.framework.threadmethod.ThreadMethodContainer;

/**
 * 
 * Proxy for {@link MethodExecutorService}
 */
public class MethodExecutorServiceProxy extends AbstractExecutorService implements ThreadExecuteTrigger{
	private MethodExecutorService methodService=null;
	private ThreadMethodContainer threadMethodContainer=null;
	private Object testCase=null;
	
	public MethodExecutorServiceProxy(ThreadMethodContainer threadMethodContainer,Object testCase) {
		this.threadMethodContainer=threadMethodContainer;
		this.testCase=testCase;
		if(threadMethodContainer==null){
			throw new IllegalArgumentException("ThreadMethodContainer object require not null.");
		}
		methodService=ExecutorServiceFactory.createMethodExecutorService();
		methodService.setThreadExecuteListener(new DefaultThreadExecuteListener());
	}
	
	public void changeThreadExecuteListener(ThreadExecuteListener executeListener){
		methodService.setThreadExecuteListener(executeListener);
	}
	
	@Override
	public void shutdown() {
		methodService.shutdown();
	}

	@Override
	public List<Runnable> shutdownNow() {
		return methodService.shutdownNow();
	}

	@Override
	public boolean isShutdown() {
		return methodService.isShutdown();
	}

	@Override
	public boolean isTerminated() {
		return methodService.isTerminated();
	}

	@Override
	public boolean awaitTermination(long timeout, TimeUnit unit)
			throws InterruptedException {
		return methodService.awaitTermination(timeout, unit);
	}

	@Override
	public void execute(Runnable command) {
		methodService.execute(command);
	}

	@Override
	public void fireThreadBefore(Thread t, Runnable r) {
		methodService.fireThreadBefore(t, r);
	}

	@Override
	public void fireThreadAllBefore() {
		methodService.fireThreadAllBefore();
	}

	@Override
	public void fireThreadGroupBefore(IThreadMethod threadMethodGroup) {
		methodService.fireThreadGroupBefore(threadMethodGroup);
	}

	@Override
	public void fireThreadGroupAfter(IThreadMethod threadMethodGroup) {
		methodService.fireThreadGroupAfter(threadMethodGroup);
	}

	@Override
	public void fireThreadAfter(Runnable r, Throwable t) {
		methodService.fireThreadAfter(r, t);
	}

	@Override
	public void fireThreadAllAfter() {
		methodService.fireThreadAllAfter();
	}

	
	public ThreadMethodContainer getThreadMethodContainer() {
		return threadMethodContainer;
	}
	
	public Object getTestCase() {
		return testCase;
	}
	
	class DefaultThreadExecuteListener implements ThreadExecuteListener {
		private final Object testcase = MethodExecutorServiceProxy.this.testCase;

		@SuppressWarnings("unchecked")
		private void invoke(Class<? extends Annotation> annotationClz) {
			if (threadMethodContainer == null)
				return;
			for (IThreadMethod threadmethod : threadMethodContainer
					.getThreadMethods(annotationClz)) {
				threadmethod.invoke(DirectExecutorService.getInstance(),
						testcase, annotationClz);
			}
		}

		@Override
		public void threadBefore(Thread t, Runnable runnable) {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + t.getName() + " execute [" + runnable
							+ "]");
			invoke(ThreadBefore.class);
		}

		@Override
		public void threadAllBefore() {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + Thread.currentThread().getName()
							+ " execute.");
			invoke(ThreadAllBefore.class);
		}

		@Override
		public void threadAllAfter() {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + Thread.currentThread().getName()
							+ " execute.");
			invoke(ThreadAllAfter.class);
		}

		@Override
		public void threadAfter(Runnable runnable, Throwable t) {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + Thread.currentThread().getName()
							+ " execute [" + runnable + "] with exception:["
							+ t + "]");
			invoke(ThreadAfter.class);
		}

		@Override
		public void threadGroupBefore(IThreadMethod threadMethodGroup) {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + Thread.currentThread().getName()
							+ " execute group:[" + threadMethodGroup + "]");
			invoke(ThreadGroupBefore.class);
		}

		@Override
		public void threadGroupAfter(IThreadMethod threadMethodGroup) {
			Logger.getLogger(this.getClass()).debug(
					"Thread name:" + Thread.currentThread().getName()
							+ " execute group:[" + threadMethodGroup + "]");
			invoke(ThreadGroupAfter.class);
		}
	}
}
